<?php

namespace Sharks\Core\Persistence\QueryBuilder;

use PDO;
use ReflectionClass;
use Sharks\Core\Persistence\Db;
use Sharks\Core\Persistence\Model;
use Sharks\Core\Persistence\Pagination\Paginator;
use Sharks\Core\Persistence\QueryBuilder\Extend\QuerySelect;
use Sharks\Core\Support\Base\Str;

/**
 * 查询构造器
 */
class SelectQuery extends AbstractExpressionQuery {

    protected $selectQuery;

    /** @var  标明是否有传入SQL 列, 当未传入时,使用默认的查询列 '*' */
    protected $hasColumn;

    protected $identifier;

    protected $takeSize;
    protected $takeOffset;

    public function __construct($identifier) {
        $this->selectQuery = new QuerySelect(Db::connector($identifier));
        $this->identifier = $identifier;
        $this->takeSize = 20;
        $this->takeOffset = 0;
    }

    /**
     * SQL having
     * @return $this
     */
    public function having() {
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'innerJoin'), $args);
        return $this;
    }

    /**
     * SQL group by
     * @return $this
     */
    public function groupBy() {
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'innerJoin'), $args);
        return $this;
    }

    /**
     * SQL inner join
     * @return $this
     */
    public function innerJoin() {
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'innerJoin'), $args);
        return $this;
    }

    /**
     * SQL left join
     * @return $this
     */
    public function leftJoin() {
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'leftJoin'), $args);
        return $this;
    }

    /**
     * SQL right join
     * @return $this
     */
    public function rightJoin() {
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'rightJoin'), $args);
        return $this;
    }

    /**
     * SQL 选择查询列
     * @return $this
     */
    public function select() {
        $this->hasColumn = true;
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'select'), $args);
        return $this;
    }

    /**
     * SQL 选择查询列: SELECT DISTINCT column ...
     * @return $this
     */
    public function selectDistinct() {
        $this->hasColumn = true;
        $args = func_get_args();
        forward_static_call_array(array($this->selectQuery, 'selectDistinct'), $args);
        return $this;
    }

    /**
     * 对应SQL 的FROM
     * @param $model
     * @return $this|\ezcQuery
     */
    public function from($model) {
        if (class_exists($model)) {
            $class = new ReflectionClass($model);
            if ($class->isSubclassOf(Model::class)) {
                $this->map($class);
                $this->selectQuery->from($class->getStaticPropertyValue('table'));
            }

        }
        elseif (is_string($model)) {
            $this->selectQuery->from($model);
        }
        return $this;
    }

    /**
     * 列排序 DESC
     * @param $column
     * @return $this
     */
    public function desc($column) {
        $this->selectQuery->orderBy($column, 'DESC');
        return $this;
    }

    /**
     * 列排序 ASC
     * @param $column
     * @return $this
     */
    public function asc($column) {
        $this->selectQuery->orderBy($column, 'ASC');
        return $this;
    }

    /**
     * 获取相应数量与偏移量的数据
     * @param $size 数量
     * @param $offset 偏移量
     */
    public function take($size, $offset = 0) {
        $this->takeSize = $size;
        $this->takeOffset = $offset;
        $this->selectQuery->limit($size, $offset);
        return $this;
    }

    /**
     * 查找
     * @param $column
     * @param $value
     * @throws \ezcQueryVariableParameterException
     */
    public function like($column, $value) {
        $this->selectQuery->where($this->selectQuery->expr->like($column, $this->selectQuery->bindValue($value)));
        return $this;
    }

    /**
     * 查询值介与两值之间
     * @param $column
     * @param $minValue
     * @param $maxValue
     * @return $this
     * @throws \ezcQueryVariableParameterException
     */
    public function between($column, $minValue, $maxValue) {
        $this->selectQuery->where($this->selectQuery->expr->between($column,
            $this->selectQuery->bindValue($minValue),
            $this->selectQuery->bindValue($maxValue)));
        return $this;
    }

    /**
     * 数据
     * @return array|bool
     * @throws \Sharks\Core\Exception\DatabaseException
     */
    public function lists() {
        if (false == $this->hasColumn) {
            $this->selectQuery->select('*');
        }
        Db::connector($this->identifier)->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
        $stmt = $this->selectQuery->prepare();
        $stmt->execute();

        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $this->checkError($stmt);
        if (false == $rows) {
            return array();
        }
        return $this->buildObjs($rows);
    }

    /**
     * 单一数据
     * @return bool|null|\Sharks\Core\Persistence\DbRecord
     * @throws \Sharks\Core\Exception\DatabaseException
     */
    public function only() {
        if (false == $this->hasColumn) {
            $this->selectQuery->select('*');
        }
        Db::connector($this->identifier)->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
        $stmt = $this->selectQuery->prepare();
        $stmt->execute();
        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        $this->checkError($stmt);
        if (false == $row) {
            return null;
        }
        return $this->buildObj($row);
    }

    /**
     * 计数查询
     * @return bool
     * @throws \Sharks\Core\Exception\DatabaseException
     */
    public function count() {
        $this->selectQuery->resetSelect('count(1)');
        return $this->pick();
    }

    /**
     * 获取单个值
     */
    public function pick() {
        $stmt = $this->selectQuery->prepare();
        $stmt->execute();
        $row = $stmt->fetch();
        $this->checkError($stmt);
        if (false == $row) {
            return null;
        }
        return $row[0];
    }

    public function paginator() {
        $data = $this->lists();
        $this->selectQuery->resetLimit();
        $totalSize = $this->count();
        return new Paginator($totalSize, $data, $this->takeSize, $this->takeOffset);

    }



    protected function getQuery() {
        return $this->selectQuery;
    }

    /**
     * 获得SQL
     * @return string
     */
    public function toSql() {
        if (!$this->hasColumn) {
            $this->select('*');
        }
        return $this->selectQuery->getQuery();
    }

}